package com.uvnos4j.matilda.system.service.impl;

import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.uvnos4j.matilda.commons.dto.QueryParam;
import com.uvnos4j.matilda.commons.exception.ServiceException;
import com.uvnos4j.matilda.system.domain.SysDept;
import com.uvnos4j.matilda.system.error.SystemError;
import com.uvnos4j.matilda.system.mapper.SysDeptMapper;
import com.uvnos4j.matilda.system.service.SysDeptService;
import com.uvnos4j.matilda.system.service.SysUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.Serializable;
import java.util.Collection;
import java.util.List;
import java.util.Objects;

/**
 * 部门 Service 实现
 * <p>
 * Description:部门 Service 实现
 * </p>
 *
 * @author Guo.wl
 * @version v1.0.0
 * @see com.uvnos4j.matilda.system.service.impl
 * @since 2020-08-31 23:14:49
 */
@Service
@Transactional(readOnly = true)
public class SysDeptServiceImpl extends ServiceImpl<SysDeptMapper, SysDept> implements SysDeptService {

    @Autowired
    private SysUserService sysUserService;

    @Override
    public List<SysDept> list(QueryParam param) {
        LambdaQueryWrapper<SysDept> lqw = new LambdaQueryWrapper<>();
        lqw.orderByAsc(SysDept::getSeq);
        return baseMapper.selectList(lqw);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean save(SysDept sysDept) {
        if (hasSameDeptAtParent(sysDept.getParentId(), sysDept.getName(), sysDept.getId())) {
            throw new ServiceException(SystemError.DEPT_REPORT_AT_SAME_PARENT);
        }
        String level = getLevel(sysDept.getParentId());
        sysDept.setLevel(level);
        return baseMapper.insert(sysDept) > 0;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateById(SysDept sysDept) {
        if (Objects.equals(sysDept.getId(), sysDept.getParentId())) {
            throw new ServiceException(SystemError.DEPT_SAME_AS_PARENT);
        }
        if (hasSameDeptAtParent(sysDept.getParentId(), sysDept.getName(), sysDept.getId())) {
            throw new ServiceException(SystemError.DEPT_REPORT_AT_SAME_PARENT);
        }
        SysDept before = baseMapper.selectById(sysDept.getId());
        if (before == null) {
            throw new ServiceException(SystemError.DEPT_NO_EXIST);
        }

        String level = getLevel(sysDept.getParentId());
        sysDept.setLevel(level);
        updateWithChild(before, sysDept);
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean removeByIds(Collection<? extends Serializable> idList) {
        // 校验
        for (Serializable id : idList) {
            SysDept dept = baseMapper.selectById(id);
            if (dept == null) {
                SystemError.DEPT_NO_EXIST.setMessage(id);
                throw new ServiceException(SystemError.DEPT_NO_EXIST);
            }
            int count = countByParentId(dept.getId());
            if (count > 0) {
                SystemError.DEPT_HAS_CHILDREN.setMessage(dept.getName());
                throw new ServiceException(SystemError.DEPT_HAS_CHILDREN);
            }
            count = sysUserService.countByDeptId(dept.getId());
            if (count > 0) {
                SystemError.DEPT_HAS_USER.setMessage(dept.getName());
                throw new ServiceException(SystemError.DEPT_HAS_USER);
            }
        }
        // 删除部门
        return baseMapper.deleteBatchIds(idList) > 0;
    }

    /**
     * 修改部门及其子部门
     *
     * @param before 修改前fromDB
     * @param after  修改后toDB
     */
    private void updateWithChild(SysDept before, SysDept after) {
        String oldLevelPrefix = before.getLevel();
        String newLevelPrefix = after.getLevel();
        if (!oldLevelPrefix.equals(newLevelPrefix)) {
            // findDeptListByLevel
            LambdaQueryWrapper<SysDept> lqw = new LambdaQueryWrapper<>();
            lqw.likeRight(SysDept::getLevel, oldLevelPrefix + StrUtil.DOT + before.getId() + StrUtil.DOT);
            List<SysDept> children = baseMapper.selectList(lqw);
            if (children != null && children.size() > 0) {
                // 更新子部门的level
                for (SysDept child : children) {
                    String level = child.getLevel();
                    if (level != null && level.indexOf(oldLevelPrefix) == 0) {
                        level = newLevelPrefix + level.substring(oldLevelPrefix.length());
                        child.setLevel(level);
                    }
                }
                updateBatchById(children, children.size());
            }
        }
        baseMapper.updateById(after);
    }

    /**
     * 根据部门父ID获取level
     *
     * @param parentId 部门父id
     * @return level
     */
    private String getLevel(Long parentId) {
        SysDept parent = baseMapper.selectById(parentId);
        if (parent == null) {
            return "0";
        }
        return StrUtil.join(StrUtil.DOT, parent.getLevel(), parentId);
    }

    /**
     * 同一上级部门下是否存在相同名称的部门
     *
     * @param parentId 上级部门id
     * @param deptName 部门名称
     * @param deptId   部门id（修改时传入）
     * @return true存在，false不存在
     */
    private boolean hasSameDeptAtParent(Long parentId, String deptName, Long deptId) {
        LambdaQueryWrapper<SysDept> lqw = new LambdaQueryWrapper<>();
        lqw.eq(SysDept::getParentId, parentId).eq(SysDept::getName, deptName);
        lqw.ne(deptId != null, SysDept::getId, deptId);
        Integer count = baseMapper.selectCount(lqw);
        return count > 0;
    }

    private Integer countByParentId(Long parentId) {
        LambdaQueryWrapper<SysDept> lqw = new LambdaQueryWrapper<>();
        lqw.eq(SysDept::getParentId, parentId);
        return baseMapper.selectCount(lqw);
    }

}